#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>

#include "uartlink.h"
#include "uart.h"
#include "debug.h"
#include "utils.h"
#include "zm32_ap.h"


typedef enum
{
    E_STATE_RX_WAIT_HEADER1,
    E_STATE_RX_WAIT_HEADER2,
    E_STATE_RX_WAIT_HEADER3,
    E_STATE_RX_WAIT_CMDID,
    E_STATE_RX_WAIT_DATA,
    E_STATE_RX_WAIT_TAIL,
} teSL_RxState;

typedef enum
{
    E_PROTO_TMP,
    E_PROTO_PST,
    E_PROTO_SPC,
} teSL_ProtoType;

typedef struct
{
    pthread_mutex_t mutex;
    int fd;                     //串口文件描述符
    utils_thread_ts reader;     //串口读线程
    utils_thread_ts writer;     //串口写线程
    utils_thread_ts cber;       //串口回调处理线程

    //读链表
    pthread_mutex_t reader_mtx;
    struct list_head reader_list;

    //写链表
    pthread_mutex_t writer_mtx;
    struct list_head writer_list;

    //回调链表
    pthread_mutex_t cber_mtx;
    struct list_head cber_list;

} tsSerialLink;


static tsSerialLink sSerialLink;


/*****************************************************************************
 函 数 名 : SL_TxByte
 功能描述 : 发送一字节串口数据
 参     数 : unsigned char u8Data  
 返 回 值 : static int
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月21日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
static int SL_TxByte( unsigned char u8Data )
{
    return eSerial_Write(u8Data);
}


/*****************************************************************************
 函 数 名 : SL_RxByte
 功能描述 : 接收一字节串口数据
 参     数 : unsigned char *pu8Data  
 返 回 值 : static bool
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月21日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
static bool SL_RxByte( unsigned char *pu8Data )
{
    if ( eSerial_Read(pu8Data) == E_SERIAL_OK )
    {
        return true;
    }
    else
    {
        LOGE("read error");
        return false;
    }
}

static void SL_FreeMessage( struct SL_Message *p )
{
    if ( NULL != p )
    {
        free(p->pu8Message);
        p->pu8Message = NULL;
        free(p);
    }
}

static void SL_FreeMessageCb( struct SL_MessageCb *p )
{
    free(p);
}

static teSL_Status SL_ReadMessage( unsigned int *pu32Type, unsigned short *pu16Length, unsigned short u16MaxLength,
                                   unsigned char *pu8Message )
{
    int ret;
    teSL_RxState eRxState = E_STATE_RX_WAIT_HEADER1;
    static teSL_ProtoType ProtoTpye;
    unsigned short u16Bytes = 0;
    unsigned char u8Data;
    unsigned int payloadsize;

    while ( SL_RxByte(&u8Data) )
    {
        switch ( eRxState )
        {
            case E_STATE_RX_WAIT_HEADER1:
                u16Bytes = 0;
                *pu32Type |= u8Data;
                eRxState++;
                break;

            case E_STATE_RX_WAIT_HEADER2:
                *pu32Type <<= 8u;
                *pu32Type |= u8Data;
                eRxState++;
                break;

            case E_STATE_RX_WAIT_HEADER3:
                *pu32Type <<= 8u;
                *pu32Type |= u8Data;
                eRxState++;
                break;

            case E_STATE_RX_WAIT_CMDID:
                *pu32Type <<= 8u;
                *pu32Type |= u8Data;
                eRxState++;
                ret = zm32_getMessageLength(*pu32Type, &payloadsize);
                if ( ret < 0 )
                {
                    return E_SL_ERROR;
                }
                break;

            case E_STATE_RX_WAIT_DATA:
                if ( u16Bytes < payloadsize )
                {
                    pu8Message[u16Bytes] = u8Data;
                }
                if (*pu32Type == E_SL_SPC_RX_FRAME && u16Bytes == 12)
                {
                    payloadsize += pu8Message[u16Bytes-2] << 8U | pu8Message[u16Bytes - 1];
                }
                if (*pu32Type == E_SL_SPC_GROUP_RX_FRAME && u16Bytes == 14)
                {
                    payloadsize += pu8Message[u16Bytes-2] << 8U | pu8Message[u16Bytes - 1];
                }

                if ( ++u16Bytes == payloadsize )
                {
                    u16Bytes = 0;
                    eRxState = E_STATE_RX_WAIT_HEADER1;
                    *pu16Length = payloadsize;
                    printf("\n\n");
                    return E_SL_OK;
                }
                break;

            default:
                break;
        }

        if ( eRxState <= E_STATE_RX_WAIT_HEADER3 )
        {
            switch ( u8Data )
            {
                case 0xDE:
                    ProtoTpye = E_PROTO_TMP;
                case 0xAB:
                    ProtoTpye = E_PROTO_PST;
                case 0xAD:
                    ProtoTpye = E_PROTO_SPC;
                    if ( eRxState != E_STATE_RX_WAIT_HEADER2 )
                    {
                        return E_SL_ERROR;
                    }
                    break;

                case 0xDF:
                    ProtoTpye = E_PROTO_TMP;
                case 0xBC:
                    ProtoTpye = E_PROTO_PST;
                case 0xDB:
                    ProtoTpye = E_PROTO_SPC;
                    if ( eRxState != E_STATE_RX_WAIT_HEADER3 )
                    {
                        return E_SL_ERROR;
                    }
                    break;

                case 0xEF:
                    ProtoTpye = E_PROTO_TMP;
                case 0xCD:
                    ProtoTpye = E_PROTO_PST;
                case 0xBE:
                    ProtoTpye = E_PROTO_SPC;
                    if ( eRxState != E_STATE_RX_WAIT_CMDID )
                    {
                        return E_SL_ERROR;
                    }
                    break;

                default:
                    break;

            }
        }
    }

    return E_SL_NOMESSAGE;
}


/*****************************************************************************
 函 数 名 : SL_WriteMessage
 功能描述 : 发送串口消息
 参     数 : unsigned int u32header       帧头
            unsigned short u16Length    payload长度
            unsigned char *pu8Data      payload指针
            unsigned char u8tail        帧尾
 返 回 值 : static teSL_Status
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月21日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
static teSL_Status SL_WriteMessage( unsigned int u32header, unsigned short u16Length, unsigned char *pu8Data )
{
    int n;

    // 帧间隔时间，ZM32模块接收数据配置时，采用时间间隔来分帧
    usleep(10000);

    /* 发送帧头 */
    printf("\n===> ");
    if ( SL_TxByte((u32header >> 24) & 0xFF) != E_SERIAL_OK )
    {
        return E_SL_ERROR;
    }
    if ( SL_TxByte((u32header >> 16) & 0xFF) != E_SERIAL_OK )
    {
        return E_SL_ERROR;
    }

    if ( SL_TxByte((u32header >> 8) & 0xFF) != E_SERIAL_OK )
    {
        return E_SL_ERROR;
    }

    if ( SL_TxByte((u32header >> 0) & 0xFF) != E_SERIAL_OK )
    {
        return E_SL_ERROR;
    }


    /* 发送消息payload */
    for ( n = 0; n < u16Length; n++ )
    {
        if ( SL_TxByte(pu8Data[n]) != E_SERIAL_OK )
        {
            return E_SL_ERROR;
        }
    }

    /* 发送帧尾 */
    if ( EXTRACT_PROTO(u32header) == ZM32_CONF_PST_ID || EXTRACT_PROTO(u32header) == ZM32_SPC_FRAME_ID )
    {
        if ( SL_TxByte(ZM32_FRAME_TAIL) != E_SERIAL_OK )
        {
            return E_SL_ERROR;
        }
    }
    printf("\n\n");

    return E_SL_OK;
}

/**
 * @brief 注册串口消息回调
 * @param u32Type 消息类型
 * @param cb 回调函数
 * @return
 */
static teSL_Status SL_RegisterMessageCb( unsigned int u32Type, void (*cb)( void * ) )
{
    struct SL_MessageCb *MessageCb;

    MessageCb = malloc(sizeof(struct SL_MessageCb));
    if ( NULL == MessageCb )
    {
        return E_SL_ERROR_NOMEM;
    }

    MessageCb->cb = cb;
    MessageCb->u32Type = u32Type;

    pthread_mutex_lock(&sSerialLink.cber_mtx);
    //插入cber_list头部
    list_add(&MessageCb->list, &sSerialLink.cber_list);
    pthread_mutex_unlock(&sSerialLink.cber_mtx);

    return E_SL_OK;
}

static teSL_Status SL_InitCallBackFun( void )
{
    SL_RegisterMessageCb(E_SL_TMP_LOC_SET_NWK_ADDR, zm32_set_nwkaddr_resp);
    SL_RegisterMessageCb(E_SL_PST_LOC_CONF_READ, zm32_get_local_config_resp);
    SL_RegisterMessageCb(E_SL_PST_LOC_AD_HOC, zm32_networking_node_resp);
    SL_RegisterMessageCb(E_SL_PST_NWK_SET_REMOTE_CONF, zm32_set_remote_config_resp);
    SL_RegisterMessageCb(E_SL_PST_LOC_COOR_STATE, zm32_get_networking_state_resp);
    SL_RegisterMessageCb(E_SL_TMP_LOC_TX_MODE, zm32_tmploc_tx_mode_resp);
    SL_RegisterMessageCb(E_SL_TMP_MAC_RX_MODE, zm32_tmpmac_rx_mode_resp);
    SL_RegisterMessageCb(E_SL_PST_LOC_PERMIT_JOIN, zm32_permit_join_resp);
    SL_RegisterMessageCb(E_SL_SPC_GROUP_RX_FRAME, zm32_spec_grprx_frame);
    SL_RegisterMessageCb(E_SL_SPC_RX_FRAME, zm32_spec_rx_frame);

    return E_SL_OK;
}

static void *pvReaderThread( void *psThreadInfoVoid )
{
    struct SL_FixMessage sFixMessage;
    LOGI("Starting reader thread");
    utils_thread_ts *psThreadInfo = (utils_thread_ts *) psThreadInfoVoid;

    while (utils_check_running(psThreadInfo))
    {
        memset(&sFixMessage, 0, sizeof(struct SL_FixMessage));
        sFixMessage.u16Length = 0xFFFF;

        //读取到一帧消息,插入reader_list头部
        if (SL_ReadMessage(&sFixMessage.u32Type, &sFixMessage.u16Length, SL_MAX_MESSAGE_LENGTH,
                           sFixMessage.pu8Message) == E_SL_OK)
        {
            struct SL_Message *psMessage;
            psMessage = malloc(sizeof(struct SL_Message));
            if ( NULL == psMessage )
            {
                LOGE("no mem");
                continue;
            }

            psMessage->u32Type = sFixMessage.u32Type;
            psMessage->u16Length = sFixMessage.u16Length;

            psMessage->pu8Message = malloc(sFixMessage.u16Length);
            if ( NULL == psMessage->pu8Message )
            {
                SL_FreeMessage(psMessage);
                LOGE("no mem");
                continue;
            }

            memcpy(psMessage->pu8Message, sFixMessage.pu8Message, sFixMessage.u16Length);
            pthread_mutex_lock(&sSerialLink.reader_mtx);
            list_add(&psMessage->list, &sSerialLink.reader_list);
            pthread_mutex_unlock(&sSerialLink.reader_mtx);
        }
        usleep(10); //取消点
    }


    LOGI("Exit reader thread");
    return NULL;
}

static void *pvWriterThread( void *psThreadInfoVoid )
{
    struct SL_Message *pMessage;
    utils_thread_ts *psThreadInfo = (utils_thread_ts *) psThreadInfoVoid;

    LOGI("Starting writer thread");

    while (utils_check_running(psThreadInfo))
    {
        pthread_mutex_lock(&sSerialLink.writer_mtx);
        if (!list_empty(&sSerialLink.writer_list))
        {
            //从尾部取出一个节点
            pMessage = list_last_entry(&sSerialLink.writer_list, struct SL_Message, list);
            if (NULL != pMessage)
            {
                if (SL_WriteMessage(pMessage->u32Type, pMessage->u16Length, pMessage->pu8Message) == E_SL_OK)
                {
                    list_del(&pMessage->list);
                    SL_FreeMessage(pMessage);
                }
                else
                {
                    LOGE("write message[0x%04X] error", pMessage->u32Type);
                }
            }
        }
        pthread_mutex_unlock(&sSerialLink.writer_mtx);
        usleep(10); //取消点
    }


    LOGI("Exit writer thread");
    return NULL;
}

static void *pvCberThread( void *psThreadInfoVoid )
{
    struct SL_Message *pMessage;
    struct list_head *pos, *tmp;
    struct SL_MessageCb *pMessageCb;
    int handled;
    utils_thread_ts *psThreadInfo = (utils_thread_ts *) psThreadInfoVoid;

    LOGI("Starting cber thread");

    while (utils_check_running(psThreadInfo))
    {
        //从reader_list取出一条消息
        pMessage = NULL;
        pthread_mutex_lock(&sSerialLink.reader_mtx);
        if (!list_empty(&sSerialLink.reader_list))
        {
            //从尾部取出一个节点
            pMessage = list_last_entry(&sSerialLink.reader_list, struct SL_Message, list);
            if (NULL != pMessage)
            {
                list_del(&pMessage->list);
            }
        }
        pthread_mutex_unlock(&sSerialLink.reader_mtx);

        if ( NULL != pMessage )
        {
            //匹配是否有回调函数
            pthread_mutex_lock(&sSerialLink.cber_mtx);
            handled = 0;
            list_for_each_safe(pos, tmp, &sSerialLink.cber_list)
            {
                pMessageCb = list_entry(pos, struct SL_MessageCb, list);
                if ( pMessageCb->u32Type == pMessage->u32Type )
                {
                    pMessageCb->cb(pMessage->pu8Message);
                    SL_FreeMessage(pMessage);
                    handled = 1;
                    break;
                }
            }
            pthread_mutex_unlock(&sSerialLink.cber_mtx);

            if (!handled)
            {
                LOGE("Message[%04X] not handled", pMessage->u32Type);
                SL_FreeMessage(pMessage);
            }
        }
        usleep(10); //取消点
    }


    LOGI("Exit cber thread");
    return NULL;
}

/*****************************************************************************
 函 数 名 : eSL_Init
 功能描述 : serial link初始化，包括对串口打开并初始化，串口接收消息队列的初始
            化，串口读线程的创建。
 参     数 : char *cpSerialDevice:设备名
            unsigned int u32BaudRate:波特率
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月21日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSL_Status eSL_Init( char *cpSerialDevice, unsigned int u32BaudRate )
{
    memset(&sSerialLink, 0x0, sizeof(sSerialLink));

    if ( eSerial_Init(cpSerialDevice, u32BaudRate, &sSerialLink.fd) != E_SERIAL_OK )
    {
        return E_SL_ERROR_SERIAL;
    }
    pthread_mutex_init(&sSerialLink.mutex, NULL);
    pthread_mutex_init(&sSerialLink.reader_mtx, NULL);
    pthread_mutex_init(&sSerialLink.writer_mtx, NULL);
    pthread_mutex_init(&sSerialLink.cber_mtx, NULL);

    INIT_LIST_HEAD(&sSerialLink.reader_list);
    INIT_LIST_HEAD(&sSerialLink.writer_list);
    INIT_LIST_HEAD(&sSerialLink.cber_list);

    /*初始化回调函数*/
    SL_InitCallBackFun();

    /*创建串口读线程*/
    sSerialLink.reader.detach_state = EN_THREAD_JOINABLE;
    if ( utils_thread_start(pvReaderThread, &sSerialLink.reader) != EN_UTILS_OK )
    {
        LOGE("Failed to start serial reader thread");
        return E_SL_ERROR;
    }

    /*创建串口写线程*/
    sSerialLink.writer.detach_state = EN_THREAD_JOINABLE;
    if ( utils_thread_start(pvWriterThread, &sSerialLink.writer) != EN_UTILS_OK )
    {
        LOGE("Failed to start serial writer thread");
        return E_SL_ERROR;
    }

    /*创建串口回调处理线程*/
    sSerialLink.cber.detach_state = EN_THREAD_JOINABLE;
    if ( utils_thread_start(pvCberThread, &sSerialLink.cber) != EN_UTILS_OK )
    {
        LOGE("Failed to start serial cber thread");
        return E_SL_ERROR;
    }

    return E_SL_OK;
}

/*****************************************************************************
 函 数 名 : eSL_Destroy
 功能描述 : 释放serial link的占用的资源
 参     数 : void  
 返 回 值 : 
 调用函数 : 
 被调函数 : 
 
 修改历史 :
  0.日     期  : 2020年5月21日
    作     者 : thomas
    修改内容 : 新生成函数

*****************************************************************************/
teSL_Status eSL_Destroy( void )
{
    struct list_head *pos, *tmp;
    struct SL_MessageCb *pMessageCb;
    struct SL_Message *pMessage;

    eSerial_Close();
    pthread_mutex_lock(&sSerialLink.mutex);

    if ( EN_UTILS_OK != utils_thread_stop(&sSerialLink.reader) )
    {
        LOGE("serial link reader thread stop error");
    }

    if ( EN_UTILS_OK != utils_thread_stop(&sSerialLink.writer) )
    {
        LOGE("serial link writer thread stop error");
    }

    if ( EN_UTILS_OK != utils_thread_stop(&sSerialLink.cber) )
    {
        LOGE("serial link cber thread stop error");
    }

    pthread_mutex_lock(&sSerialLink.reader_mtx);
    //free list
    list_for_each_safe(pos, tmp, &sSerialLink.reader_list)
    {
        pMessage = list_entry(pos, struct SL_Message, list);
        SL_FreeMessage(pMessage);
    }
    pthread_mutex_unlock(&sSerialLink.reader_mtx);
    pthread_mutex_destroy(&sSerialLink.reader_mtx);

    pthread_mutex_lock(&sSerialLink.writer_mtx);
    //free list
    list_for_each_safe(pos, tmp, &sSerialLink.writer_list)
    {
        pMessage = list_entry(pos, struct SL_Message, list);
        SL_FreeMessage(pMessage);
    }
    pthread_mutex_unlock(&sSerialLink.writer_mtx);
    pthread_mutex_destroy(&sSerialLink.writer_mtx);

    pthread_mutex_lock(&sSerialLink.cber_mtx);
    //free list
    list_for_each_safe(pos, tmp, &sSerialLink.cber_list)
    {
        pMessageCb = list_entry(pos, struct SL_MessageCb, list);
        SL_FreeMessageCb(pMessageCb);
    }
    pthread_mutex_unlock(&sSerialLink.cber_mtx);
    pthread_mutex_destroy(&sSerialLink.cber_mtx);

    pthread_mutex_unlock(&sSerialLink.mutex);
    pthread_mutex_destroy(&sSerialLink.mutex);

    return E_SL_OK;
}

/**
 * @brief 发送串口消息
 * @param u32Type
 * @param u16Length
 * @param pu8Message
 * @return
 */
teSL_Status eSL_SendMessage( unsigned int u32Type, unsigned short u16Length, void *pu8Message )
{
    struct SL_Message *Message;

    Message = calloc(1, sizeof(struct SL_Message));
    if ( NULL == Message )
    {
        return E_SL_ERROR_NOMEM;
    }

    if ( NULL != pu8Message )
    {
        Message->pu8Message = calloc(1, u16Length);
        if ( NULL == Message->pu8Message )
        {
            SL_FreeMessage(Message);
            return E_SL_ERROR_NOMEM;
        }
        memcpy(Message->pu8Message, pu8Message, u16Length);
    }

    Message->u32Type = u32Type;
    Message->u16Length = u16Length;

    pthread_mutex_lock(&sSerialLink.writer_mtx);
    //插入writer_list头部
    list_add(&Message->list, &sSerialLink.writer_list);
    pthread_mutex_unlock(&sSerialLink.writer_mtx);

    return E_SL_OK;
}
